home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
skills.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
35KB
|
1,069 lines
/* Initial coding: 6 Sep 1994, Nick Williams (njw@cs.city.ac.uk) */
/* Generalized code + added hiding and lockpicking skills, */
/* March 3, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
/* Added more skills, fixed bug in stealing code */
/* April 21, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
/* Added more skills, fixed bugs, see skills.h */
/* May/June, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
/* July 95 Code re-vamped. Now we add the experience objects, all
* player activities which gain experience will bbe through the use
* of skillls. Thus, I added hand_weapons, missile_weapons, and
* remove_traps skills -b.t.
*/
/* Aug 95 - Added more skills (disarm traps, spellcasting, praying).
* Also, hand_weapons is now "melee_weapons". b.t.
*/
/* Oct 95 - changed the praying skill to accomodate MULTIPLE_GODS
* hack - b.t.
*/
/* Dec 95 - modified the literacy and inscription (writing) skills. b.t.
*/
/* Mar 96 - modified the stealing skill. Objects with type FLESH or
* w/o a type cannot be stolen by players. b.t.
*/
#include <global.h>
#include <object.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <skills.h>
#include <spells.h>
#include <book.h>
/*
* When stealing: dependent on the intelligence/wisdom of whom you're
* stealing from (op in attempt_steal), offset by your dexterity and
* skill at stealing. They may notice your attempt, whether successful
* or not.
*/
int
attempt_steal(object* op, object* who)
{
object* success = 0; /* did we get anything? */
int alarmed = 0; /* was the thief caught? */
int chance, lvl;
int stats_value = get_weighted_skill_stats(who);
int lbonus = who->type==PLAYER ? who->chosen_skill->level/2 : 10;
object* tmp;
object* next;
if (op->type == PLAYER || QUERY_FLAG(op, FLAG_MONSTER)) {
/* Go thru their inventory, stealing */
for(tmp = op->inv; tmp != NULL; tmp = next) {
next = tmp->below;
/* you can't steal worn items, starting items, wiz stuff,
* innate abilities, flesh or items w/o a type. Generally
* speaking, the invisibility flag prevents experience or
* abilities from being stolen since these types are currently
* always invisible archs. I was implicit here so as to prevent
* future possible problems. -b.t. */
if (QUERY_FLAG(tmp,FLAG_WAS_WIZ) || QUERY_FLAG(tmp, FLAG_APPLIED)
|| !(tmp->type) || tmp->type == FLESH
|| tmp->type == EXPERIENCE || tmp->type == ABILITY
|| QUERY_FLAG(tmp,FLAG_STARTEQUIP)
|| tmp->invisible )
continue;
/* Okay, try stealing this item. Dependent on dexterity of thief */
/* NYI: Also, dependent on value of thievery skill */
/* The current calculation gives a random number around 15 */
/* Note -- its probably much harder to steal from hostile
* beings! - alter the chances of success to reflect this
*/
if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
chance = (RANDOM()%30+RANDOM()%30)/2;
else
chance = (RANDOM()%60+RANDOM()%60)/2;
/* Nov 95 - toss in a level adjustment too. Its harder to steal
* from higher level beings. */
lvl = op->level>lbonus ? op->level/2 : 0;
if (chance < (stats_value + lbonus - lvl)) {
pick_up(who, tmp);
/* If you steal something heavy off them, they're bound to notice */
if (tmp->weight > (200*(RANDOM()%(stats_value+lbonus))-(RANDOM()*5+1))) {
alarmed = 1;
}
if(can_pick(who,tmp)) success = tmp;
break;
}
}
}
if (alarmed || RANDOM()%25 > stats_value) {
/* play_sound("stop! thief!"); kindofthing */
if (op->type != PLAYER) {
/* The unaggressives look after themselves 8) */
CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
if(who->type==PLAYER) npc_call_help(op);
} else {
char buf[MAX_BUF];
/* Notify the other player */
if (success && who->stats.Int > RANDOM()%20) {
sprintf(buf, "Your %s has gone missing!", query_name(success));
} else {
sprintf(buf, "Your pack feels strangely lighter.");
}
new_draw_info(NDI_UNIQUE, 0,op,buf);
if (!success) {
if (who->invisible) {
sprintf(buf, "you feel itchy fingers getting at your pack.");
} else {
sprintf(buf, "%s looks very shifty.", query_name(who));
}
new_draw_info(NDI_UNIQUE, 0,op,buf);
}
}
}
return success? 1:0;
}
int steal(object* op, int dir)
{
object *tmp, *next;
int x = op->x + freearr_x[dir];
int y = op->y + freearr_y[dir];
if(dir == 0) {
/* Can't steal from ourself! */
return 0;
}
if(wall(op->map,x,y)) {
return 0;
}
/* Find the topmost object at this spot */
for(tmp = get_map_ob(op->map,x,y);
tmp != NULL && tmp->above != NULL;
tmp = tmp->above);
/* For all the stacked objects at this point, attempt a steal */
for(; tmp != NULL; tmp = next) {
next = tmp->below;
/* Minor hack--for multi square beings - make sure we get
* the 'head' coz 'tail' objects have no inventory! - b.t.
*/
if (tmp->head) tmp=tmp->head;
if (attempt_steal(tmp, op)) {
/* no experience for stealing from another player */
if(tmp->type==PLAYER)
return 0;
else
return (calc_skill_exp(op,tmp));
}
}
return 0;
}
/* Implementation by bt. (thomas@nomad.astro.psu.edu)
* monster implementation 7-7-95 by bt.
*/
int pick_lock(object *pl, int dir)
{
char buf[MAX_BUF];
object *tmp;
int x = pl->x + freearr_x[dir];
int y = pl->y + freearr_y[dir];
int success = 0;
if(!dir) dir=pl->facing;
/* For all the stacked objects at this point find a door*/
sprintf(buf, "There is no lock there.");
for(tmp=get_map_ob(pl->map,x,y); tmp; tmp=tmp->above) {
if(!tmp) continue;
switch(tmp->type) {
case DOOR:
if (attempt_pick_lock(tmp, pl)) {
success = 1;
sprintf(buf, "you pick the lock.");
} else
sprintf(buf, "you fail to pick the lock.");
break;
case LOCKED_DOOR:
sprintf(buf, "you can't pick that lock!");
break;
default:
break;
}
}
new_draw_info(NDI_UNIQUE, 0,pl,buf);
if(success)
return calc_skill_exp(pl,NULL);
else
return 0;
}
int attempt_pick_lock ( object *door, object *pl)
{
int bonus = SK_level(pl);
int difficulty= pl->map->difficulty ? pl->map->difficulty : 0;
int dex = get_skill_stat1(pl) ? get_skill_stat1(pl) : 10;
int success = 0, number; /* did we get anything? */
/* If has can_pass set, then its not locked! */
if(!QUERY_FLAG(door,FLAG_NO_PASS)) return 0;
/* Try to pick the lock on this item (doors only for now).
* Dependent on dexterity/skill SK_level of the player and
* the map level difficulty.
*/
number = (RANDOM()%40+RANDOM()%40)/2;
if (number < ((dex + bonus) - difficulty)) {
remove_door(door);
success = 1;
} else if (door->inv && door->inv->type==RUNE) { /* set off any traps? */
spring_trap(door->inv,pl);
}
return success;
}
/* HIDE CODE. Right now, user becomes 'invisible' for
* a short while (success and duration dependant on player SK_level,
* dexterity, charisma, and map difficulty
* Players have a good chance of becoming 'unhidden' if they move
* and like invisiblity will be come visible if they attack
* Implemented by b.t. (thomas@nomad.astro.psu.edu)
* July 7, 1995 - made hiding possible for monsters. -b.t.
*/
int hide(object *op) {
char buf[MAX_BUF];
int level= SK_level(op);
/* the preliminaries -- Can we really hide now? */
/* this keeps monsters from using invisibilty spells and hiding */
if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
sprintf(buf,"You don't need to hide while invisible!");
new_draw_info(NDI_UNIQUE, 0,op,buf);
return 0;
} else if (!op->hide && op->invisible>0 && op->type == PLAYER) {
sprintf(buf,"Your attempt to hide breaks the invisibility spell!");
op->invisible= 0;
op->contr->tmp_invis=0;
if(QUERY_FLAG(op, FLAG_UNDEAD)) CLEAR_FLAG(op, FLAG_UNDEAD);
new_draw_info(NDI_UNIQUE, 0,op,buf);
update_object(op);
return 0;
}
if(op->invisible>(50*level)) {
new_draw_info(NDI_UNIQUE,0,op,"You are as well hidden as you can get.");
return 0;
}
if(attempt_hide(op)) {
new_draw_info(NDI_UNIQUE, 0,op,"You hide in the shadows.");
update_object(op);
return calc_skill_exp(op, NULL);
}
new_draw_info(NDI_UNIQUE,0,op,"You fail to conceal yourself.");
return 0;
}
int attempt_hide(object *op) {
int level = SK_level(op)/5;
int difficulty=op->map->difficulty;
int number;
int dexterity = get_skill_stat1(op);
int charisma = get_skill_stat2(op)/2;
int success = 0;
/* safety. perhaps its a monster that is hiding */
charisma = charisma ? charisma : 10;
dexterity = dexterity ? dexterity : 15;
/* Hiding success and duration dependant on SK_level,
* dexterity, charisma, and map difficulty
*/
number = (RANDOM()%20+RANDOM()%30)/2;
if (number < (dexterity + level - difficulty - (charisma/2))) {
success = 1;
op->invisible += 100; /* set the level of 'hiddeness' */
if(op->type==PLAYER)
op->contr->tmp_invis=1;
op->hide=1;
}
return success;
}
/* jump() - this is both a new type of movement for player/monsters and
* an attack as well. -b.t.
*/
int jump(object *pl, int dir)
{
char buf[MAX_BUF];
int spaces=0,stats;
int str = get_skill_stat1(pl);
int dex = get_skill_stat2(pl);
dex = dex ? dex : 15;
str = str ? str : 10;
stats=str*str*str*dex;
if(pl->carrying!=0) /* don't want div by zero !! */
spaces=(int) (stats/pl->carrying);
else
spaces=2; /* pl has no objects - gets the far jump */
if(spaces>2)
spaces = 2;
else if(spaces==0) {
sprintf(buf, "You are carrying too much weight to jump.");
new_draw_info(NDI_UNIQUE, 0,pl,buf);
return 0;
}
return attempt_jump(pl,dir,spaces);
}
int attempt_jump (object *pl, int dir, int spaces) {
char buf[MAX_BUF];
object *tmp;
int i,exp=0,dx=freearr_x[dir],dy=freearr_y[dir];
/* Jump loop. Go through spaces opject wants to jump. Halt the
* jump if a wall or creature is in the way. We set FLAG_FLYING
* temporarily to allow player to aviod exits/archs that are not
* fly_on, fly_off. This will also prevent pickup of objects
* while jumping over them.
*/
remove_ob(pl);
SET_FLAG(pl,FLAG_FLYING);
for(i=0;i<=spaces;i++) {
for(tmp=get_map_ob(pl->map,pl->x+dx,pl->y+dy);
tmp;tmp=tmp->above) {
if(wall(tmp->map,tmp->x,tmp->y)) { /* Jump into wall*/
sprintf(buf, "Your jump is blocked.");
new_draw_info(NDI_UNIQUE, 0,pl,buf);
(void) stop_jump(pl,i,spaces);
return 0;
}
if(QUERY_FLAG(tmp,FLAG_MONSTER) /* Jump into creature */
|| tmp->type==PLAYER ) {
sprintf(buf, "You jump into%s%s.",
tmp->type == PLAYER ? " " : " the ", tmp->name);
new_draw_info(NDI_UNIQUE, 0,pl,buf);
#ifdef SIMPLE_PARTY_SYSTEM
if(tmp->type!=PLAYER || pl->contr->party_number==-1 ||
pl->contr->party_number!=tmp->contr->party_number)
#endif
exp = skill_attack(tmp,pl,pl->facing,"kicked"); /* pl makes an attack */
(void) stop_jump(pl,i,spaces);
return exp; /* note that calc_skill_exp() is already called by skill_attack() */
}
if(tmp->type==EXIT /* pl jump through exit */
&& QUERY_FLAG(tmp, FLAG_FLY_ON)) {
pl->x+=dx,pl->y+=dy;
(void) stop_jump(pl,i,spaces);
return calc_skill_exp(pl,NULL);
}
}
if(out_of_map(pl->map,pl->x+dx,pl->y+dy)) {
(void) stop_jump(pl,i,spaces);
return calc_skill_exp(pl,NULL);
} else
pl->x+=dx,pl->y+=dy;
}
(void) stop_jump(pl,i,spaces);
return calc_skill_exp(pl,NULL);
}
/* stop_jump() - End of jump. Clear flags, restore the map, and
* freeze the jumper a while to simulate the exhaustion
* of jumping.
*/
int stop_jump(object *pl, int dist, int spaces) {
/* int load=dist/(pl->speed*spaces); */
CLEAR_FLAG(pl,FLAG_FLYING);
insert_ob_in_map(pl,pl->map);
draw(pl);
/* pl->speed_left= (int) -FABS((load*8)+1); */
return 0;
}
/* skill_ident() - this code is supposed to allow players to identify
* classes of objects with the various "auto-ident" skills. Player must
* have unidentified objects of the right type in order for the skill
* to work. While multiple classes of objects may be identified,
* this code is kind of yucky -- it would be nice to make it a bit
* more generalized. Right now, skill indices are embedded in this routine.
* Returns amount of experience gained (on successful ident).
* - b.t. (thomas@astro.psu.edu)
*/
int skill_ident(object *pl) {
char buf[MAX_BUF];
int success=0;
if(!pl->chosen_skill) /* should'nt happen... */
return 0;
if(pl->type != PLAYER) return 0; /* only players will skill-identify */
sprintf(buf, "You look at the objects you are carrying...");
new_draw_info(NDI_UNIQUE, 0,pl,buf);
switch (pl->chosen_skill->stats.sp) {
case SK_SMITH:
success += do_skill_ident(pl,WEAPON) + do_skill_ident(pl,ARMOUR)
+ do_skill_ident(pl,BRACERS) + do_skill_ident(pl,CLOAK)
+ do_skill_ident(pl,BOOTS) + do_skill_ident(pl,SHIELD)
+ do_skill_ident(pl,GIRDLE) + do_skill_ident(pl,HELMET)
+ do_skill_ident(pl,GLOVES);
break;
case SK_BOWYER:
success += do_skill_ident(pl,BOW) + do_skill_ident(pl,ARROW);
break;
case SK_ALCHEMY:
success += do_skill_ident(pl,POTION) + do_skill_ident(pl,POISON)
+ do_skill_ident(pl,AMULET) + do_skill_ident(pl,CONTAINER)
+ do_skill_ident(pl,DRINK) + do_skill_ident(pl,INORGANIC);
break;
case SK_WOODSMAN:
success += do_skill_ident(pl,FOOD) + do_skill_ident(pl,DRINK)
+ do_skill_ident(pl,FLESH);
break;
case SK_JEWELER:
success += do_skill_ident(pl,GEM) + do_skill_ident(pl,RING);
break;
case SK_LITERACY:
success += do_skill_ident(pl,SPELLBOOK)
+ do_skill_ident(pl,SCROLL) + do_skill_ident(pl,BOOK);
break;
case SK_THAUMATURGY:
success += do_skill_ident(pl,WAND) + do_skill_ident(pl,ROD)
+ do_skill_ident(pl,HORN);
break;
case SK_DET_CURSE:
success = do_skill_detect_curse(pl);
if(success)
new_draw_info(NDI_UNIQUE, 0,pl,"...and discover cursed items!");
break;
case SK_DET_MAGIC:
success = do_skill_detect_magic(pl);
if(success)
new_draw_info(NDI_UNIQUE, 0,pl,
"...and discover items imbued with mystic forces!");
break;
default:
LOG(llevError,"Error: bad call to skill_ident()");
return 0;
break;
}
if(success && pl->type == PLAYER)
draw_inventory(pl);
else if(!success) {
sprintf(buf,"...and learn nothing more.");
new_draw_info(NDI_UNIQUE, 0,pl,buf);
}
return success;
}
int do_skill_detect_curse(object *pl) {
object *tmp;
int success=0;
/* check the player inventory - stop after 1st success or
* run out of unidented items
*/
for(tmp=pl->inv;tmp;tmp=tmp->below)
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_CURSED)
&& (QUERY_FLAG(tmp,FLAG_CURSED) || QUERY_FLAG(tmp,FLAG_DAMNED)) ) {
SET_FLAG(tmp,FLAG_KNOWN_CURSED);
success+=calc_skill_exp(pl,tmp);
}
return success;
}
int do_skill_detect_magic(object *pl) {
object *tmp;
int success=0;
/* check the player inventory - stop after 1st success or
* run out of unidented items
*/
for(tmp=pl->inv;tmp;tmp=tmp->below)
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)
&& (is_magical(tmp) || always_magical(tmp)) ) {
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
success+=calc_skill_exp(pl,tmp);
}
return success;
}
/* do_skill_ident() - workhorse for skill_ident() -b.t. */
/* Sept 95. I put in a probability for identification of artifacts.
* highly magical artifacts will be more difficult to ident -b.t.
*/
int do_skill_ident(object *pl, int obj_class) {
object *tmp;
int success=0,chance;
int skill_value = SK_level(pl) + get_weighted_skill_stats(pl);
/* check the player inventory */
for(tmp=pl->inv;tmp;tmp=tmp->below)
if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_NO_SKILL_IDENT)
&& need_identify(tmp)
&& !tmp->invisible && tmp->type==obj_class) {
chance = RANDOM()%10+RANDOM()%10+RANDOM()%10+
RANDOM()%(tmp->magic ? tmp->magic*5 : 1);
if(skill_value >= chance) {
identify(tmp);
if (pl->type==PLAYER) {
new_draw_info_format(NDI_UNIQUE, 0, pl,
"You have %s.", long_desc(tmp));
if (tmp->msg) {
new_draw_info(NDI_UNIQUE, 0,pl, "The item has a story:");
new_draw_info(NDI_UNIQUE, 0,pl, tmp->msg);
}
if (pl->contr->eric_server > 0)
esrv_send_item(pl, tmp);
}
success += calc_skill_exp(pl,tmp);
} else
SET_FLAG(tmp, FLAG_NO_SKILL_IDENT);
}
return success;
}
/* players using this skill can 'charm' a monster --
* into working for them. It can only be used on
* non-special (see below) 'neutral' creatures.
* -b.t. (thomas@nomad.astro.psu.edu)
*/
int use_oratory(object *pl, int dir) {
int expsum=0,x=pl->x+freearr_x[dir],y=pl->y+freearr_y[dir];
int stat1 = get_skill_stat1(pl);
object *tmp;
if(pl->type!=PLAYER) return 0; /* only players use this skill */
for(tmp=get_map_ob(pl->map,x,y);tmp;tmp=tmp->above) {
if(!tmp) return 0;
if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue;
if(tmp->type==PLAYER) continue; /* can't persude players! */
new_draw_info_format(NDI_UNIQUE,
0,pl, "You orate to the %s.",query_name(tmp));
/* the following conditions limit who may be 'charmed' */
if(!QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) && /* it's hostile! */
!QUERY_FLAG(tmp, FLAG_FRIENDLY)) {
new_draw_info_format(NDI_UNIQUE, 0,pl,
"Too bad the %s is'nt listening!\n",query_name(tmp));
return 0;
}
/* it's already allied! */
if(QUERY_FLAG(tmp,FLAG_FRIENDLY)&&(tmp->move_type==PETMOVE)){
if(get_owner(tmp)==pl) {
new_draw_info(NDI_UNIQUE, 0,pl,
"Your follower loves your speach.\n");
return 0;
} else if(SK_level(pl)>tmp->level) { /* you steal the follower! */
set_owner(tmp,pl);
new_draw_info_format(NDI_UNIQUE, 0,pl,
"You convince the %s to follow you instead!\n"
,query_name(tmp));
return calc_skill_exp(pl,tmp);
}
}
if(tmp->more || tmp->head) continue; /* no multiple square monsters*/
if(tmp->msg) continue; /* no special message monsters */
/* Ok, got a 'sucker' lets try to make them a follower */
if((tmp->level+1)<(SK_level(pl)+RANDOM()%(stat1))) {
new_draw_info_format(NDI_UNIQUE, 0,pl,
"You convince the %s to become your follower.\n"
,query_name(tmp));
set_owner(tmp,pl);
SET_FLAG(tmp,FLAG_MONSTER);
tmp->stats.exp = 0;
add_friendly_object(tmp);
SET_FLAG(tmp,FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
expsum += calc_skill_exp(pl,tmp);
}
/* maybe we made it angry */
if((SK_level(pl)+((stat1-10)/2))<RANDOM()%((2*tmp->level)+1)) {
new_draw_info_format(NDI_UNIQUE, 0,pl,
"Your speach angers the %s!\n",query_name(tmp));
if(QUERY_FLAG(tmp,FLAG_FRIENDLY)) {
CLEAR_FLAG(tmp,FLAG_FRIENDLY);
tmp->move_type = 0; /* needed? */
}
CLEAR_FLAG(tmp,FLAG_UNAGGRESSIVE);
}
}
return expsum;
}
/* Singing() -this skill allows the player to pacify nearby creatures.
* There are few limitations on who/what kind of
* non-player creatures that may be pacified. Right now, a player
* may pacify creatures which have Int == 0. In this routine, once
* successfully pacified the creature gets Int=1. Thus, a player
* may only pacify a creature once.
* BTW, I appologize for the naming of the skill, I couldnt think
* of anything better! -b.t.
*/
int singing(object *pl, int dir) {
int i,exp = 0,stat1=get_skill_stat1(pl);
object *tmp;
if(pl->type!=PLAYER) return 0; /* only players use this skill */
new_draw_info_format(NDI_UNIQUE,0,pl, "You sing");
for(i=dir;i<(dir+MIN(SK_level(pl),SIZEOFFREE));i++)
for(tmp=get_map_ob(pl->map,pl->x+freearr_x[i],pl->y+freearr_y[i]);
tmp;tmp=tmp->above) {
if(!tmp) return 0;
if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue;
/* can't affect players */
if(tmp->type==PLAYER || tmp->stats.Int) continue;
/* the following monsters can't be calmed */
if(QUERY_FLAG(tmp,FLAG_SPLITTING) /* have no ears! */
|| QUERY_FLAG(tmp,FLAG_HITBACK)) break;
if(tmp->stats.Int>0) break; /* is too smart */
if(tmp->level>SK_level(pl)) break; /* too powerfull */
if(QUERY_FLAG(tmp,FLAG_UNDEAD)) /* undead dont listen! */
break;
if(QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) /* already calm */
||QUERY_FLAG(tmp,FLAG_FRIENDLY))
break;
if((tmp->level)<((RANDOM()%((SK_level(pl)+1)*2))+(stat1-9)/2)){
SET_FLAG(tmp,FLAG_UNAGGRESSIVE);
new_draw_info_format(NDI_UNIQUE, 0,pl,
"You calm down the %s\n",query_name(tmp));
tmp->stats.Int = 1; /* this prevents re-pacification */
exp += calc_skill_exp(pl,tmp);
} else {
new_draw_info_format(NDI_UNIQUE, 0,pl,
"Too bad the %s is'nt listening!\n",query_name(tmp));
}
}
return exp;
}
/* The FIND_TRAPS skill. This routine is taken mostly from the
* command_search loop. It seemed easier to have a separate command,
* rather than overhaul the existing code - this makes sure things
* still work for those people who don't want to have skill code
* implemented.
*/
int find_traps (object *pl) {
object *tmp,*tmp2;
int i,expsum=0;
/*First we search all around us for runes and traps, which are
all type RUNE */
for(i=0;i<9;i++) {
/* Check everything in the square for trapness */
if(out_of_map(pl->map,pl->x + freearr_x[i],pl->y + freearr_y[i])) continue;
for(tmp = get_map_ob(pl->map, pl->x + freearr_x[i], pl->y +freearr_y[i]);
tmp!=NULL;tmp=tmp->above) {
/* And now we'd better do an inventory traversal of each
of these objects' inventory */
for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below)
if(tmp2->type==RUNE)
if(trap_see(pl,tmp2)) {
trap_show(tmp2,tmp);
if(tmp2->stats.Cha>1) {
expsum += calc_skill_exp(pl,tmp2);
/* do the following so calc_skill_exp will know
* how much xp to award for disarming*/
tmp2->stats.exp = tmp2->stats.Cha * tmp2->level;
tmp2->stats.Cha = 1; /* unhide the trap */
}
}
if(tmp->type==RUNE)
if(trap_see(pl,tmp)) {
trap_show(tmp,tmp);
if(tmp->stats.Cha>1) {
expsum += calc_skill_exp(pl,tmp);
tmp->stats.exp = tmp->stats.Cha * tmp->level;
tmp->stats.Cha = 1; /* unhide the trap */
}
}
}
}
return expsum;
}
/* pray() - when this skill is called from do_skill(), it allows
* the player to regain lost grace points at a faster rate. -b.t.
*/
/* Oct 95 - altered it to allow for MULTIPLE_GODS hack */
int pray (object *pl) {
char buf[MAX_BUF];
sprintf(buf,"You pray.");
if(pl->type!=PLAYER) return 0;
#ifdef MULTIPLE_GODS
else { /* look at the top object underneath the player, is it an altar? */
object *tmp=pl->below;
if(tmp && !strcmp(tmp->arch->name,"altar"))
if(tmp && tmp->title && pl->chosen_skill->exp_obj) { /* its owned by a God! */
char *pl_god = determine_god(pl);
sprintf(buf,"You pray at the altar.");
/* hmm. what happend depends on pl previous god, level, etc */
if(!strcmp(pl_god,"none")||lookup_god_by_name(pl_god)==-1) { /*new convert */
become_follower(pl,tmp->title);
return 0;
} else if(!strcmp(pl_god,tmp->title)) { /* pray at your gods altar */
int bonus = ((pl->stats.Wis/10)+(SK_level(pl)/10));
/* we can get neg grace up faster */
if(pl->stats.grace<0) pl->stats.grace+=(bonus>-1*(pl->stats.grace/10) ?
bonus : -1*(pl->stats.grace/10));
/* we can super-charge grace to 2x max */
if(pl->stats.grace<(2*pl->stats.maxgrace)) {
pl->stats.grace+=bonus/2;
sprintf(buf,"%s is pleased with your worship.",pl_god);
}
/* Every once in a while, the god decides to checkup on their
* follower, and may intervene to help them out. */
if((RANDOM()%500-bonus)<0)
god_intervention(pl,pl_god);
} else if(tmp->title) { /* praying to another god! */
int loss = 0;
new_draw_info_format(NDI_UNIQUE|NDI_NAVY,0,pl,
"Heretic! %s is angered!",pl_god);
/* whether we will be successfull in defecting or not -
* we lose experience from the clerical experience obj */
loss = -0.1 * (float) pl->chosen_skill->exp_obj->stats.exp;
loss = check_dm_add_exp_to_obj(pl->chosen_skill->exp_obj,loss);
pl->chosen_skill->exp_obj->stats.exp += loss;
pl->stats.exp += loss;
add_exp(pl,0);
draw_stats(pl);
/* May switch Gods, but its random chance based on our current level
* note it gets harder to swap gods the higher we get */
if(!(RANDOM()%pl->chosen_skill->exp_obj->level))
become_follower(pl,tmp->title);
return 0;
}
}
}
#endif
new_draw_info(NDI_UNIQUE,0,pl,buf);
if(pl->stats.grace < pl->stats.maxgrace) {
pl->stats.grace++;
pl->last_grace = -1;
} else return 0;
do_some_living(pl);
return 0;
}
/* This skill allows the player to regain a few sp or hp for a
* brief period of concentration. No armour or weapons may be
* wielded/applied for this to work. The amount of time needed
* to concentrate and the # of points regained is dependant on
* the level of the user. - b.t. thomas@astro.psu.edu
*/
/* July 95 I commented out 'factor' - this should now be handled by
* get_skill_time() -b.t. */
/* Sept 95. Now meditation is level dependant (score). User may
* meditate w/ more armour on as they get higher level
* Probably a better way to do this is based on overall encumberance
* -b.t.
*/
void meditate (object *pl) {
object *tmp;
int lvl = pl->level;
/* int factor = 10/(1+(pl->level/10)+(pl->stats.Int/15)+(pl->stats.Wis/15)); */
if(pl->type!=PLAYER) return; /* players only */
/* check if pl has removed encumbering armour and weapons */
if(QUERY_FLAG(pl,FLAG_READY_WEAPON) && (lvl<6)) {
new_draw_info(NDI_UNIQUE,0,pl,
"You can't concentrate while wielding a weapon!\n");
return;
} else {
for(tmp=pl->inv;tmp;tmp=tmp->below)
if(( (tmp->type==ARMOUR && lvl<12)
|| (tmp->type==HELMET && lvl<10)
|| (tmp->type==SHIELD && lvl<6)
|| (tmp->type==BOOTS && lvl<4)
|| (tmp->type==GLOVES && lvl<2) )
&& QUERY_FLAG(tmp,FLAG_APPLIED)) {
new_draw_info(NDI_UNIQUE,0,pl,
"You can't concentrate while wearing so much armour!\n");
return;
}
}
/* ok let's meditate! Spell points are regained first, then once
* they are maxed we get back hp. Actual incrementing of values
* is handled by the do_some_living() (in player.c). This way magical
* bonuses for healing/sp regeneration are included properly
* No matter what, we will eat up some playing time trying to
* meditate. (see 'factor' variable for what sets the amount of time)
*/
new_draw_info(NDI_UNIQUE,0,pl, "You meditate.");
/* pl->speed_left -= (int) FABS(factor); */
if(pl->stats.sp < pl->stats.maxsp) {
pl->stats.sp++;
pl->last_sp = -1;
} else if (pl->stats.hp < pl->stats.maxhp) {
pl->stats.hp++;
pl->last_heal = -1;
} else return;
do_some_living(pl);
}
/* write_on_item() - wrapper for write_note and write_scroll */
int write_on_item (object *pl,char *params) {
object *item=pl->inv;
if(pl->type!=PLAYER) return 0;
/* Need to be able to read before we can write! */
if(!find_skill(pl,SK_LITERACY)) {
new_draw_info(NDI_UNIQUE,0,pl,
"You must learn to read before you can write!");
return 0;
}
/* Check if we are ready to attempt inscription */
if(item) {
if(QUERY_FLAG(item,FLAG_UNPAID)) {
new_draw_info(NDI_UNIQUE,0,pl,
"You had better pay for that before you write on it.");
return 0;
}
switch(item->type) {
case SCROLL:
return write_scroll(pl);
break;
case BOOK: {
char *string=params;
int i;
/* if skill name occurs at begining of the string
* we have to reset pointer to miss it */
if(lookup_skill_by_name(params)>=0)
for(i=(strcspn(string," ")+1);i>0;i--) string++;
return write_note(pl,item,string);
break;
}
default:
break;
}
}
new_draw_info_format(NDI_UNIQUE,0,pl,"You cannot write on %s",
!item?"nothing":query_short_name(item));
return 0;
}
/* write_note() - this routine allows players to inscribe messages in
* ordinary 'books' (anything that is type BOOK). b.t.
*/
int write_note(object *pl, object *item, char *msg) {
char buf[BOOK_BUF];
if(!item||item->type!=BOOK) return 0;
if(!msg) {
new_draw_info(NDI_UNIQUE,0,pl,"No message to write!");
new_draw_info(NDI_UNIQUE,0,pl,"Usage: use_skill inscription <message>");
return 0;
}
if(!book_overflow(item->msg,msg,BOOK_BUF)) { /* add msg string to book */
if(item->msg) {
strcpy(buf,item->msg);
strcat(buf,"\n"); /* new msg goes on a new line */
free_string(item->msg);
}
strcat(buf,msg);
item->msg=add_string(buf);
new_draw_info_format(NDI_UNIQUE,0,pl,
"You write in the %s.",query_short_name(item));
return strlen(msg);
} else
new_draw_info_format(NDI_UNIQUE,0,pl,
"Your message won't fit in the %s!",query_short_name(item));
return 0;
}
/* write_scroll() - this routine allows players to inscribe spell scrolls
* of spells which they know. Backfire effects are possible with the
* severity of the backlash correlated with the difficulty of the scroll
* that is attempted. -b.t. thomas@astro.psu.edu
*/
int write_scroll (object *pl) {
object *scroll=pl->inv;
int success=0,confused=0,chosen_spell=-1,stat1=get_skill_stat1(pl);
/* Check if we are ready to attempt inscription */
chosen_spell=pl->contr->chosen_spell;
if(chosen_spell<0) {
new_draw_info(NDI_UNIQUE,0,pl,
"You need a spell readied in order to inscribe!");
return 0;
}
if(spells[chosen_spell].scroll_chance==0) { /* Tried to write non-scroll spell */
new_draw_info_format(NDI_UNIQUE,0,pl,"You can't inscribe the spell %s.",
spells[chosen_spell].name);
return 0;
}
if(spells[chosen_spell].sp>pl->stats.sp) {
new_draw_info_format(NDI_UNIQUE,0,pl,
"You don't have enough spell points to write a scroll of %s."
,spells[chosen_spell].name);
return 0;
}
/* ok, we are ready to try inscription */
if(QUERY_FLAG(pl,FLAG_CONFUSED)) confused = 1;
success = RANDOM()%(spells[chosen_spell].level*4) < SK_level(pl) ? 1 : 0;
pl->stats.sp-=spells[chosen_spell].sp; /* lose sp no matter what */
if(success) {
if(scroll->nrof) scroll->nrof = 1;
if(!confused) {
scroll->level= (SK_level(pl)>spells[chosen_spell].level ?
SK_level(pl) : spells[chosen_spell].level);
} else { /* a confused scribe gets a random spell */
chosen_spell=0;
while (!spells[chosen_spell].scroll_chance)
chosen_spell=RANDOM()%NROFREALSPELLS;
scroll->level=SK_level(pl)>spells[chosen_spell].level ?
spells[chosen_spell].level : ((RANDOM()%SK_level(pl))+1);
}
if(scroll->stats.sp==chosen_spell)
new_draw_info(NDI_UNIQUE,0,pl,
"You overwrite the scroll.");
else {
new_draw_info(NDI_UNIQUE,0,pl,
"You succeed in writing a new scroll.");
scroll->stats.sp=chosen_spell;
}
draw_inventory(pl);
success = calc_skill_exp(pl,scroll);
if(!confused) success *= 2;
return success;
} else
if(spells[chosen_spell].level>SK_level(pl) || confused){ /*backfire!*/
new_draw_info(NDI_UNIQUE,0,pl,
"Ouch! Your attempt to write a new scroll strains your mind!");
if(RANDOM()%2==1)
drain_specific_stat(pl,4);
else {
confuse_player(pl,pl,99);
return (-3*calc_skill_exp(pl,scroll));
}
} else if(RANDOM()%stat1<15) {
new_draw_info(NDI_UNIQUE,0,pl,
"Your attempt to write a new scroll rattles your mind!");
confuse_player(pl,pl,99);
} else
new_draw_info(NDI_UNIQUE,0,pl,"You fail to write a new scroll.");
return (-1*calc_skill_exp(pl,scroll));
}
/* remove_trap() - This skill will disarm any previously discovered trap
* the algorithm is based (almost totally) on the old command_disarm() - b.t.
*/
int remove_trap (object *op, int dir) {
object *tmp,*tmp2;
int i,x,y,success=0;
for(i=0;i<9;i++) {
x = op->x + freearr_x[i];
y = op->y + freearr_y[i];
if(out_of_map(op->map,x,y)) {
new_draw_info(NDI_UNIQUE,0,op,"There are no traps there!");
return 0;
}
/* Check everything in the square for trapness */
for(tmp = get_map_ob(op->map,x,y);tmp!=NULL;tmp=tmp->above) {
/* And now we'd better do an inventory traversal of each
* of these objects' inventory */
for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below)
if(tmp2->type==RUNE&&tmp2->stats.Cha<=1) {
trap_show(tmp2,tmp);
if(trap_disarm(op,tmp2,1))
success += calc_skill_exp(op,tmp2);
}
if(tmp->type==RUNE&&tmp->stats.Cha<=1) {
trap_show(tmp,tmp);
if (trap_disarm(op,tmp,1))
success += calc_skill_exp(op,tmp);
}
}
}
return success;
}